package net.xmmpp.uc.im.internal.web.config;

import com.google.common.io.ByteStreams;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@Component
@Order(10)
public class LogFilter extends OncePerRequestFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(LogFilter.class);

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

        HttpServletRequestExt request = new HttpServletRequestExt(httpServletRequest);
        String requestURI = request.getRequestURI();

        if (requestURI.startsWith("/actuator")) {
            filterChain.doFilter(request, httpServletResponse);
            return;
        }

        StringBuilder strBuilder = new StringBuilder();
        strBuilder.append("Request start at: ").append(LocalDateTime.now()).append('\n')
                .append("Request url: ").append(requestURI).append('\n')
                .append("Request method: ").append(request.getMethod()).append('\n')
                .append("Request headers: ").append('\n');

        final Enumeration<String> headerNames = request.getHeaderNames();
        String headerName = null;
        String headerValue = null;
        while (headerNames.hasMoreElements()) {
            headerName = headerNames.nextElement();
            headerValue = request.getHeader(headerName);
            strBuilder.append('\t').append(headerName).append(": ").append(headerValue).append('\n');
        }

        strBuilder.append("Request query string: ").append(request.getQueryString()).append('\n')
                .append("Request body: ").append(new String(request.body)).append('\n');

        if (!request.getMethod().equalsIgnoreCase("HEAD")) {
            LOGGER.info(strBuilder.toString());
        }

        filterChain.doFilter(request, httpServletResponse);
    }

    public class HttpServletRequestExt extends HttpServletRequestWrapper {

        private final byte[] body;

        private String queryString;

        private String token;

        private Map<String, String[]> parameterMap = new HashMap<>();

        public HttpServletRequestExt(HttpServletRequest request) throws IOException {
            super(request);
            body = ByteStreams.toByteArray(request.getInputStream());

            String sign = request.getParameter("sign");
            if (StringUtils.isEmpty(sign)) {
                queryString = request.getQueryString();
                parameterMap = request.getParameterMap();
            } else {

            }
        }

        @Override
        public String getQueryString() {
            return queryString;
        }

        @Override
        public String getHeader(String name) {
            if ("access-token".equalsIgnoreCase(name) && token != null) {
                return token;
            } else {
                return super.getHeader(name);
            }
        }

        @Override
        public Map<String, String[]> getParameterMap() {
            return parameterMap;
        }

        @Override
        public String getParameter(String name) {
            if (parameterMap.containsKey(name)) {
                return parameterMap.get(name)[0];
            }
            return null;
        }

        @Override
        public Enumeration<String> getParameterNames() {
            return super.getParameterNames();
        }

        @Override
        public String[] getParameterValues(String name) {
            return parameterMap.get(name);
        }

        @Override
        public ServletInputStream getInputStream() throws IOException {
            ByteArrayInputStream in = new ByteArrayInputStream(body);
            return new ServletInputStream() {
                @Override
                public boolean isFinished() {
                    return false;
                }

                @Override
                public boolean isReady() {
                    return false;
                }

                @Override
                public void setReadListener(ReadListener readListener) {

                }

                @Override
                public int read() throws IOException {
                    return in.read();
                }
            };
        }
    }
}
